/*************************************************************************
	> File Name: client.cpp
	> Author: xuwenlong
	> Mail: myxuan475@126.com 
	> Created Time: 2018年09月17日 星期一 15时02分07秒
 ************************************************************************/

#include "client.h"
#include <event2/thread.h>
#include "dasserver.h"
#include <map>
#include <sys/stat.h>
#include <sys/types.h>
#include <sstream>
#include <utils/dfile.h>
#include <transport/http/http.h>
#include <utils/shellcmd.h>

Client::Client(DasServer *pServer):
    User(pServer,new JsonRpcHandler(this)),
    m_iTelId(-1),
    m_icheck(0),
    m_ftpPort(0),
    m_telnetPort(0)
{
}

Client::~Client()
{
}

BOOL Client::OnReceiveJson(S32 index,DAS_RESULT status, const string &method, const Json::Value &result)
{
    if (status != DAS_SUCCESS) {
        return TRUE;
    }
//    if (method == "startDaemon") {
//        m_version = result["version"].asString();
//        m_strId = result["gateway_id"].asString();
//        m_company = result["company"].asString();
//        ((DasServer*)m_pServer)->addCompany(m_company,m_strId,this);
//    }
    return TRUE;
}

DAS_RESULT Client::registerAcquisitor(const Json::Value &params,Json::Value &result)
{
    m_version = params["version"].asString();
    m_strId = params["gateway_id"].asString();
    m_company = params["company"].asString();
    m_ipAddr = params["ipaddr"].asString();
    ((DasServer*)m_pServer)->addCompany(m_company,m_strId,this);
    result = true;
    char dir[MAX_NAME_LEN];
    sprintf(dir,"%s/log/%s",
            getenv(CONFIG_DIR_ENV),m_company.c_str());
    mkdir(dir,0777);
    return DAS_SUCCESS;
}

DAS_RESULT Client::uploadFile(const Json::Value &params, Json::Value &result)
{
    char dir[MAX_NAME_LEN];
    char file[MAX_NAME_LEN];

    const string &name = params["name"].asString();
    const string &content = params["content"].asString();
    int seek = params["seek"].asInt();

    sprintf(dir,"%s/log/%s/%s",
            getenv(CONFIG_DIR_ENV),m_company.c_str(),m_strId.c_str());
    mkdir(dir,0777);

    //检查是否已满
    string check_log = check_disk_log(dir);

    sprintf(file,"%s/%s",dir,name.c_str());

    FILE *fp;
    if (access(file, R_OK) != 0) {//not exists
      fp = fopen(file, "w+");
    } else  {
      fp = fopen(file, "r+");
    }

    if (!fp) {
        result = 0;
        return DAS_FILE_OPENFAILD;
    }

    //
    if (check_log != name) {
        fseek(fp,seek,SEEK_SET);
        int wlen = fwrite(content.c_str(),1,content.length(),fp);

        fseek(fp,0,SEEK_END);
        long int filelen = ftell(fp);
        if (seek+wlen >= filelen)
            result = wlen;
        else
            result = (int)(filelen-seek);
    }
    else {
        fseek(fp,0,SEEK_SET);
        int wlen = fwrite(content.c_str(),1,content.length(),fp);
        result = wlen;
    }

    fclose(fp);
    return DAS_SUCCESS;
}

DAS_RESULT Client::reportError(const Json::Value &params)
{
    if (!IsRegister())
        return DAS_SUCCESS;

    cl_report rp,rb;
    rp.dev_count = params["count"].asInt();
    rp.acq_last = params["acqlast"].asUInt64();
    rp.acq_fail = params["acqfail"].asInt();
    rp.up_last = params["uplast"].asUInt64();
    rp.up_fail = params["upfail"].asInt();
    rp.uptime = 0;


    int len = 50+strlen(getenv(CONFIG_DIR_ENV));
    char *filename = (char*)malloc(len);
    memset(filename,0,len);
    sprintf(filename,"%s/data/%s.stat",
            getenv(CONFIG_DIR_ENV),m_strId.c_str());

    if(!utils::file_exist(filename))
    {
        utils::save_file(filename,0,(char*)&rp,1,sizeof(rp));
        free(filename);
        return DAS_SUCCESS;
    }
    if (utils::load_file(filename,0,(char*)&rb,1,sizeof(rb)) < 0) {
        free(filename);
        return DAS_SUCCESS;
    }
    //报告
    if (rp.up_last/60 <= 60 ||
            (rp.acq_fail <= rb.acq_fail &&
             rp.up_fail <= rb.up_fail )) {
        free(filename);
        return DAS_SUCCESS;
    }

//#ifdef LOCAL_TEST
//    Http http("https://oapi.dingtalk.com/robot/send?access_token=b3da6004c4bc3e1ca87904697184b7e941d8284ba91364ae332a9a8045b64cd8");
//#else
    Http http("https://oapi.dingtalk.com/robot/send?access_token=ddc6a2cbf26e97bad7e8a2a85f8b41cc5b815de9bda78d163dedf32c46b3f58c");
//#endif
    Json::Value param;
    Json::Value markdown;

    rp.uptime = time(NULL);// 上传

    param["msgtype"] = "markdown";

    string title,text;
    title = m_company+"网关出现问题";
    markdown["title"] = title;

    text ="# "+title+"\n";
//    text += "(网关汇报仅作参考，对于因网络问题未上报的项目和网关可能遗漏)\n";

    stringstream ss;
//    ss << "### " << "*项目名称:* "<<m_company << "\n";
    ss << "### " << "*网关ID:* "<<m_strId << "\n";
    ss << "### " << "*网关IP:* "<<m_ipAddr<<"\n";
    ss << "### " << "*上报时间:* "<<utils::timetostr(rp.uptime)<<"\n";
//    ss << "## " << "*设备总数:* "<<rp.dev_count<<"\n";
    ss << "### " << "*上报问题:* \n";
    if (rp.acq_fail > rb.acq_fail)
        ss <<"- 发现"<<rp.acq_fail<< "个设备通讯异常,最晚一次采集发生在"<<utils::timetostr(rp.uptime-3500,3)<<"点\n";
    if (rp.up_fail > rb.up_fail)
        ss <<"- 有"<<rp.up_fail<<"个数据长时间没有上传,最晚一次上传在"<<utils::timetostr(rp.uptime-3500,3)<<"点\n";
    ss << "\n";
    text += ss.str();
    markdown["text"] = text;

    param["markdown"] = markdown;

    http.PostJson(param);

    //-------------------------------------------------
    utils::save_file(filename,0,(char*)&rp,1,sizeof(rp));
    free(filename);
    return DAS_SUCCESS;
}

S32 Client::SendJsonRpc(JsonRpcInterface *_interface,const std::string &method, const Json::Value &params)
{
    if (method == "telCmd") {
        if (!m_pProtocolHandler->SendAsyncJson(m_iTelId,_interface,method,params))
            return (m_iTelId=-1);
        return m_iTelId;
    }
    else
        return m_pProtocolHandler->SendJsonRpc(_interface,method,params);
}

void Client::startDaemon()
{
    Json::Value params;
    sockaddr_in *addr = (sockaddr_in*)&m_iaddr;
    params["ipaddr"] = inet_ntoa(addr->sin_addr);
    params["port"] = ntohs(addr->sin_port);
    m_pProtocolHandler->SendJsonRpc(this,"startDaemon",params);
}

Json::Value Client::JsonAcquisitor()
{
    Json::Value acq;
    acq["version"] = m_version;
    acq["gatewayId"] = m_strId;
    acq["company"] = m_company;

    sockaddr_in *addr = (sockaddr_in*)&m_iaddr;
    acq["ipaddr"] = m_ipAddr;//inet_ntoa(addr->sin_addr);
//    acq["port"] = ntohs(addr->sin_port);
    acq["netaddr"] = inet_ntoa(addr->sin_addr);
    acq["ftpport"] = m_ftpPort;
    acq["telport"] = m_telnetPort;
    return acq;
}

const string &Client::CompanyName()
{
    return m_company;
}

const string &Client::GatewayId()
{
    return m_strId;
}

bool Client::IsRegister()
{
    if (m_strId.empty()) {
        m_icheck--;
        return false;
    }
    m_icheck = 0;
    return true;
}

int Client::CheckCount()
{
    if (m_icheck >= 0)
        return 0;
    return -m_icheck;
}

bool Client::IsIdEqual(const std::string &strId)
{
    return (m_strId == strId);
}

string Client::Report(const string &mark)
{
#if 0
    stringstream ss;
    if (dev_count == -1) {
        ss << "##### (最近没有上报问题,请检查网络是否通畅)\n";
        return ss.str();
    }
    ss << mark << "*上报时间:* "<<utils::timetostr(uptime)<<"\n";
    ss << mark << "*设备总数:* "<<dev_count<<"\n";
    if (up_last/60 <= 60 && acq_fail == 0)
        ss << mark << "*采集状态:* "<< "无设备采集异常\n";
    else
        ss << mark << "*采集状态:* "<<acq_fail << "个设备采集异常,最严重设备最后一次采集发生在"<<utils::timetostr(uptime-(acq_last>up_last?acq_last:up_last),3)<<"时之前\n";
    if (up_last/60 <= 60 && up_fail < 10)
        ss << mark << "*上传状态:* "<< "无设备上传异常\n";
    else
        ss << mark << "*上传状态:* "<<acq_fail << "个设备没有上传,共"<<up_fail<<"个数据,最严重设备最后一次上传发生在"<<utils::timetostr(uptime-up_last,3)<<"时之前\n";
    ss << "\n";
    return ss.str();
#endif
    return "";
}

int Client::GetPort(int type)
{
    string url;
    if (type == 1)
        url ="http://127.0.0.1:7500/api/port/tcp/getport/"+m_strId;
    else if (type == 2)
        url = "http://127.0.0.1:7500/api/port/tcp/getftpport/"+m_strId;
    else
        return 0;

    int port = getport(url);
    if (type == 1)
        m_telnetPort = port;
    else if (type == 2)
        m_ftpPort = port;
    return port;
}

string Client::timedifftostr(U64 min)
{
    U64 mod;
    string units[] = {"分钟","小时","天"};
    int gans[] = {60,24,10000};
    stack<string> stackstr;

    for (int i = 0;i < 3;i++) {
        if (min == 0)
            break;

        mod = min%gans[i];
        if (mod != 0) {
            stringstream str;
            str << min%gans[i] << units[i];
            stackstr.push(str.str());
        }
        min = min/gans[i];
    }

    string str;
    while (!stackstr.empty()) {
        str += (string)stackstr.top();
        stackstr.pop();
    }
    return str;
}

string Client::timetostr(time_t t)
{
    char szTime[50] = {0};
    struct tm *tm2;

    tm2 = localtime(&t);

    strftime(szTime,sizeof(szTime),"%Y-%m-%d %H",tm2);
    return string(szTime);
}

int Client::getport(const string &url)
{
    string script= "curl "+url;
    string telrlt = ShellCmd().ShellGet(script.c_str());
    Json::Value result = json_string_to_json(telrlt.c_str());

    if (!result.isMember("port") || !result.isMember("code"))
        return 0;
    if (result["code"].asInt() != 0)
        return 0;
    return result["port"].asInt();
}

Json::Value Client::json_string_to_json(const char *str)
{
    Json::Value output;
    std::string errs;

    Json::CharReaderBuilder builder;
    builder["collectComments"] = false;
    Json::CharReader *reader = builder.newCharReader();

    const char *pStr = str;
    int   length = strlen(str);
    if (reader->parse(pStr,pStr+length,&output,&errs)) {
        delete reader;
        return output;
    }

    delete reader;
    return output;
}

